<?php

class Common_DataGateway_Database {
	
    private $str_resource = null;
    private $obj_logger = null;
    
    static public $arr_databases = array();

    public function __construct($str_key = null) {
            $this->str_resource = $str_key;
    }

    public function setLogger(Zend_Log $obj_logger) {
        $this->obj_logger = $obj_logger;
    }

    public function getLogger() {
        return $this->obj_logger;
    }

    public function hasLogger() {
        return !is_null($this->obj_logger);
    }

    protected function log($message, $priority = null) {
        if($this->hasLogger()) {
            $priority = Zend_Log::DEBUG;
            $this->getLogger()->log($message, $priority);
        }
    }

    /**
     *
     * @return Zend_Db_Select
     */
    public function select($str_key = null) {
        return $this->getResource($str_key)->select();
    }
    
    public function query($mix_statement, $str_key = null) {

        if(is_string($mix_statement)) {
            $this->log($mix_statement);
        } else if(is_object($mix_statement)) {
            $this->log($mix_statement->__toString());
        }

        $mix = $this->getResource($str_key)->query($mix_statement);
        return $mix;
    }
    
    public function quote($mix, $str_key = null) {
        $mix = $this->getResource($str_key)->quote($mix);
        return $mix;
    }
    
    public function insert($str_table, $arr_data, $str_key = null) {

        $arr_inserts = array();
        foreach($arr_data as $mix_value) {
            $str_insert = null;
            if(is_numeric($mix_value)) {
                $str_insert = $mix_value;
            } else if(is_string($mix_value)) {
                $str_insert = "'" . $mix_value . "'";
            } else if(is_null($mix_value)) {
                $str_insert = "NULL";
            }
            $arr_inserts[] = $str_insert;
        }

        $str_sql = 'INSERT INTO ' . $str_table . ' (' . implode(', ', array_keys($arr_data)) . ') VALUES (' . implode(', ', $arr_inserts) . ')';
        $this->log($str_sql);

        return $this->getResource($str_key)->insert($str_table, $arr_data);
    }

    public function insertIgnore($str_table, $arr_data, $str_key = null) {

        $arr_inserts = array();
        foreach($arr_data as $mix_value) {
            $str_insert = null;
            if(is_numeric($mix_value)) {
                $str_insert = $mix_value;
            } else if(is_string($mix_value)) {
                $str_insert = "'" . $mix_value . "'";
            } else if(is_null($mix_value)) {
                $str_insert = "NULL";
            }
            $arr_inserts[] = $str_insert;
        }

        $str_sql = 'INSERT IGNORE INTO ' . $str_table . ' (' . implode(', ', array_keys($arr_data)) . ') VALUES (' . implode(', ', $arr_inserts) . ')';
        $this->log($str_sql);

        return $this->getResource($str_key)->query($str_sql);
    }
    
    protected function lastInsertId($str_key = null) {
        return $this->getResource($str_key)->lastInsertId();
    }
    
    public function update($str_table, $arr_data, $mix_where = null, $str_key = null) {

        $str_sql = 'UPDATE ' . $str_table;

        $arr_updates = array();
        foreach($arr_data as $str_attribute => $mix_value) {
            $str_update = null;
            if(is_numeric($mix_value)) {
                $str_update = $str_attribute . ' = ' . $mix_value;
            } else if(is_string($mix_value)) {
                $str_update = $str_attribute . " = '" . $mix_value . "'";
            } else if(is_null($mix_value)) {
                $str_update = $str_attribute . " = NULL";
            }
            $arr_updates[] = $str_update;
        }

        $str_sql .= ' ' . implode(', ', $arr_updates) . ' ';


        if(!empty($mix_where)) {
            $str_sql .= 'WHERE ' . $mix_where;
        }
        $this->log($str_sql);

        return $this->getResource($str_key)->update($str_table, $arr_data, $mix_where);
    }
    
    public function delete($str_table, $mix_where = null, $str_key = null) {
        return $this->getResource($str_key)->delete($str_table, $mix_where);
    }
    
    public function beginTransaction($str_key = null) {
        $this->getResource($str_key)->beginTransaction();
    }
    
    public function commit($str_key = null) {
        $this->getResource($str_key)->commit();
    }
    
    public function rollback($str_key = null) {
        $this->getResource($str_key)->rollBack();
    }
    
    public function getDefaultResource() {
        return $this->str_resource;
    }

    public function setDefaultResource($str_key) {
        $this->str_resource = $str_key;
    }
    
    /**
     * Simply escapes a variable in terms of MySQL injection attacks. Should be
     * used before inserting *any* arbitrary data manually into a query.
     *
     * @param str str_value  Value to escape
     */
    public function escapeValue($str_value) {
        return $this->getResource()->quote($str_value);
    }
	
    /**
     * Determines whether the resource key exists - either to reference
     * preparation data or the actual database object resource
     *
     * @access public
     * @param string $str_key The named database resource key
     * @param array $arr_data The connection data as necessary
     * @return void
     */
    public function defineResource($str_key, $arr_data) {
        self::$arr_databases[$str_key] = $arr_data;
    }
	
    /**
     * Adds the Zend_DB_Adapter resource to the collection of database connections.
     *
     * @access public
     * @param string $str_key The named database resource key
     * @param object $obj_database The database adapter resource
     * @return void
     */
	public function addResource($str_key, $obj_database) {
		if($obj_database instanceof Zend_Db_Adapter_Abstract) {
            self::$arr_databases[$str_key] = $obj_database;
        }
	}
    
    /**
     * Determines whether the resource key exists - either to reference
     * preparation data or the actual database object resource
     *
     * @access public
     * @param string $str_key The named database resource key
     * @return boolean
     */
    public static function issetResource($str_key) {
        return array_key_exists($str_key, self::$arr_databases) &&
            isset(self::$arr_databases[$str_key]);
    }
	
    /**
     * Retrieves the database resource using lazy initialisation
     *
     * @access protected
     * @param string $str_key The named database resource key
     * @return object
     */
	public function getResource($str_key = null)
    {
		$obj_resource = null;
		
        // case: if no specific key assigned, used the default key
        if ($str_key == null) {
            $str_key = $this->getDefaultResource();
        }

        // case: determine if the specified resource key exists
		if (self::issetResource($str_key)) {
			$arr_temp = self::$arr_databases[$str_key];
			
            // case: the database has been prepared, but not initialised 
			if (is_array($arr_temp)) {
                // initialise the resource and store for future access
				$obj_resource = $this->initialiseResource($arr_temp);
                if ($obj_resource) {
                    $this->addResource($str_key, $obj_resource);
                }
			}
            // case: the database is already initialised, return object
            else {
                $obj_resource = $this->retrieveResource($str_key);
            }
		}
		
		return $obj_resource;
	}
    
    static private function retrieveResource($str_key) {
        if(self::issetResource($str_key)) {
            return self::$arr_databases[$str_key];
        }
        return null;
    }
    
    static private function initialiseResource($arr_data) {

        if(!isset($arr_data['adapter']) || is_null($arr_data['adapter'])) {
            return null;
        }

        // Ensure we're using UTF-8
        $arr_data['charset'] = 'utf8';

        $str_adapter = $arr_data['adapter'];
        
        $obj_resource = Zend_Db::factory($str_adapter, $arr_data);
        
        return $obj_resource;
    }
    
    static public function valueIn($str_attribute, $mix) {
        $str = null;
        
        $arr_parameters_temp = array();
        
        if(is_numeric($mix)) {
            $str = $str_attribute . ' = ' . $mix;
        } else if(is_string($mix)) {
            $str = $str_attribute . " = '" . $mix . "'";
        } else if(is_array($mix)) {
                    
            // if there is one value in the array, use recursion to define a single value clause
            if(count($mix) == 1) {
                return self::valueIn($str_attribute, array_pop($mix));
            }
            
            $arr_placeholders = array();
            foreach($mix as $mix_data) {
                if(is_numeric($mix_data)) {
                    $arr_placeholders[] = $mix_data;
                } else if(is_string($mix_data)) {
                    $arr_placeholders[] = "'" . $mix_data . "'";
                }
            }
            
            $str = $str_attribute . ' IN (' . implode($arr_placeholders, ', ') . ')';
        }
        
        return $str;
    }

    static public function valueNotIn($str_attribute, $mix) {
        $str = null;

        $arr_parameters_temp = array();

        if(is_numeric($mix)) {
            $str = $str_attribute . ' != ' . $mix;
        } else if(is_string($mix)) {
            $str = $str_attribute . " != '" . $mix . "'";
        } else if(is_array($mix)) {

            // if there is one value in the array, use recursion to define a single value clause
            if(count($mix) == 1) {
                return self::valueNotIn($str_attribute, array_pop($mix));
            }

            $arr_placeholders = array();
            foreach($mix as $mix_data) {
                if(is_numeric($mix_data)) {
                    $arr_placeholders[] = $mix_data;
                } else if(is_string($mix_data)) {
                    $arr_placeholders[] = "'" . $mix_data . "'";
                }
            }

            $str = $str_attribute . ' NOT IN (' . implode($arr_placeholders, ', ') . ')';
        }

        return $str;
    }

    static public function generateUnionOfTables($str_sql, $arr_tables) {

        $str_query = $str_sql;

        $arr_sql = array();
        foreach($arr_tables as $str_table) {
            $arr_sql[] = str_replace('__TABLE__', $str_table, $str_sql);
        }

        if(!empty($arr_sql)) {
            $str_query = '(' . implode(') UNION ALL (', $arr_sql) . ')';
        }

        return $str_query;

    }
    
    static public function isDuplicateEntryException(Exception $obj_exception) {

        $bln_duplicate_exception = false;
        
        $str_message = $obj_exception->getMessage();
        $int_code = $obj_exception->getCode();

        if(($int_code == 23000) && preg_match('/.* 1062 .*/i', $str_message)) {
            $bln_duplicate_exception = true;
        }

        return $bln_duplicate_exception;
        
    }
}

